package cn.yinyu.queue.module.ecg.service.queuesequence;

import cn.yinyu.queue.module.ecg.Utils;
import cn.yinyu.queue.module.ecg.dal.dataobject.checktype.CheckTypeDO;
import cn.yinyu.queue.module.ecg.dal.mysql.checktype.CheckTypeMapper;
import cn.yinyu.queue.module.infra.api.config.ConfigApi;
import cn.yinyu.queue.module.system.api.dict.DictDataApi;
import cn.yinyu.queue.module.system.api.dict.dto.DictDataRespDTO;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalTime;
import java.util.*;
import cn.yinyu.queue.module.ecg.controller.admin.queuesequence.vo.*;
import cn.yinyu.queue.module.ecg.dal.dataobject.queuesequence.QueueSequenceDO;
import cn.yinyu.queue.framework.common.pojo.PageResult;
import cn.yinyu.queue.framework.common.pojo.PageParam;
import cn.yinyu.queue.framework.common.util.object.BeanUtils;

import cn.yinyu.queue.module.ecg.dal.mysql.queuesequence.QueueSequenceMapper;

import javax.annotation.Resource;

import static cn.yinyu.queue.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.yinyu.queue.module.ecg.Constants.*;
import static cn.yinyu.queue.module.ecg.enums.ErrorCodeConstants.*;

/**
 * 当天序号 Service 实现类
 *
 * @author 金华医院
 */
@Service
@Validated
public class QueueSequenceServiceImpl implements QueueSequenceService {

    @Resource
    private ConfigApi configApi;

    @Resource
    private DictDataApi dictDataApi;

    @Resource
    private QueueSequenceMapper queueSequenceMapper;

    @Resource
    private CheckTypeMapper checkTypeMapper;

    @Override
    public void initQueueSequenceTable() {
        queueSequenceMapper.clearQueueSequenceTableNotCurrent();
        Integer rowCount = queueSequenceMapper.getQueueSequenceTableRowCount();
        if (null == rowCount || 0 == rowCount) {
            resetQueueSequence();
        }
    }

    @Override
    public Integer createQueueSequence(QueueSequenceSaveReqVO createReqVO) {
        // 插入
        QueueSequenceDO queueSequence = BeanUtils.toBean(createReqVO, QueueSequenceDO.class);
        queueSequenceMapper.insert(queueSequence);
        // 返回
        return queueSequence.getId();
    }

    @Override
    public void updateQueueSequence(QueueSequenceSaveReqVO updateReqVO) {
        // 校验存在
        validateQueueSequenceExists(updateReqVO.getId());
        // 更新
        QueueSequenceDO updateObj = BeanUtils.toBean(updateReqVO, QueueSequenceDO.class);
        queueSequenceMapper.updateById(updateObj);
    }

    @Override
    public void deleteQueueSequence(Integer id) {
        // 校验存在
        validateQueueSequenceExists(id);
        // 删除
        queueSequenceMapper.deleteById(id);
    }

    private void validateQueueSequenceExists(Integer id) {
        if (queueSequenceMapper.selectById(id) == null) {
            throw exception(QUEUE_SEQUENCE_NOT_EXISTS);
        }
    }

    @Override
    public QueueSequenceDO getQueueSequence(Integer id) {
        return queueSequenceMapper.selectById(id);
    }

    @Override
    public List<QueueSequenceDO> selectGivenCheckTypeTimeslot(Integer checkType) {
        return queueSequenceMapper.selectTimeslotByCheckType( checkType );
    }

    @Override
    public PageResult<QueueSequenceDO> getQueueSequencePage(QueueSequencePageReqVO pageReqVO) {
        return queueSequenceMapper.selectPage(pageReqVO);
    }

    @Override
    public void resetQueueSequence() {
        queueSequenceMapper.clearQueueSequenceTable();

        // 读取时段配置
        String strBookTimeslotLength = configApi.getConfigValueByKey(BOOK_TIMESLOT_LENGTH);

        // 方案1：配置中取    7:30,8:30,9:30,10:30,11:30,12:30,13:30,14:30,15:30
        //String strBookTimeslotList = configApi.getConfigValueByKey(BOOK_TIMESLOT_LIST);
        //List<LocalTime> timeslotList = Utils.parseTimeSlotList(strBookTimeslotList, Integer.valueOf(strBookTimeslotLength));

        List<DictDataRespDTO> dictBookTimeslotList = dictDataApi.getDictDataList(ECG_BOOK_TIMESLOT);

        // 读取所有的检查类型
        List<CheckTypeDO> checkTypeDOList =  checkTypeMapper.simpleCheckTypeList();
        checkTypeDOList.forEach( checkTypeDO -> {

            for (int i=0; i < dictBookTimeslotList.size(); i++) {
                QueueSequenceDO queueSequenceDO = new QueueSequenceDO();
                queueSequenceDO.setCheckType( checkTypeDO.getValue() );
                queueSequenceDO.setTimeSlot( Integer.valueOf(dictBookTimeslotList.get(i).getValue()) );
                queueSequenceDO.setQueueNo( i * checkTypeDO.getTimeslotBookNum() + checkTypeDO.getTimeslotReservedNum());
                queueSequenceDO.setQueueVipNo( i * checkTypeDO.getTimeslotBookNum());
                queueSequenceDO.setQueueFull( i * checkTypeDO.getTimeslotBookNum() + checkTypeDO.getTimeslotBookNum());
                queueSequenceDO.setQueueVipFull( i * checkTypeDO.getTimeslotBookNum() + checkTypeDO.getTimeslotReservedNum());
                queueSequenceMapper.insert( queueSequenceDO );
            };
        });
    }

    @Override
    @Transactional
    public Integer distributeSeqNo(Integer checkType, Integer timeslot) {
        Integer curSeqNo = queueSequenceMapper.selectQueueNoForUpdate(checkType, timeslot);
        if (null == curSeqNo) {
            // 从下一个时段 获取序号。原因系统用30分钟的间隔划分预约时间段，HIS的预约时间段可能是30分钟或60分钟
            Integer nextTimeslot = getNextTimeSlot(timeslot, checkType);
            if (null == nextTimeslot) {
                throw exception(QUEUE_SEQUENCE_TIMESLOT_FULL);
            }

            curSeqNo = queueSequenceMapper.selectQueueNoForUpdate(checkType, nextTimeslot);
            if (null == curSeqNo) {
                throw exception(QUEUE_SEQUENCE_TIMESLOT_FULL);
            }

            Integer updateRowNum = queueSequenceMapper.updateGivenCheckTypeTimeslotSeqNo(checkType, nextTimeslot, curSeqNo);
            if (null == updateRowNum || updateRowNum == 0) {
                throw exception(QUEUE_SEQUENCE_TIMESLOT_EXCEPTION);
            }

        } else {
            Integer updateRowNum = queueSequenceMapper.updateGivenCheckTypeTimeslotSeqNo(checkType, timeslot, curSeqNo);
            if (null == updateRowNum || updateRowNum == 0) {
                throw exception(QUEUE_SEQUENCE_TIMESLOT_EXCEPTION);
            }
        }

        return curSeqNo + 1;
    }

    @Override
    @Transactional
    public Integer distributeVipSeqNo(Integer checkType, Integer timeslot) {
        Integer curVipSeqNo = queueSequenceMapper.selectQueueVipNoForUpdate(checkType, timeslot);
        if (null == curVipSeqNo) {
            throw exception(QUEUE_SEQUENCE_TIMESLOT_FULL);
        }

        Integer updateRowNum = queueSequenceMapper.updateGivenCheckTypeTimeslotVipSeqNo(checkType, timeslot, curVipSeqNo);
        if (null == updateRowNum || updateRowNum == 0) {
            throw exception(QUEUE_SEQUENCE_TIMESLOT_EXCEPTION);
        }

        return curVipSeqNo + 1;
    }

    private Integer getNextTimeSlot(Integer timeslot, Integer checkType) {
        List<DictDataRespDTO> dictBookTimeslotList = dictDataApi.getDictDataList(ECG_BOOK_TIMESLOT);
        Optional<Integer> index = dictBookTimeslotList.stream()
                .filter(e -> e.getValue().equals(String.valueOf(timeslot)))
                .findFirst()
                .map(dictBookTimeslotList::indexOf);
        if (!index.isPresent())
            return null;

        if (dictBookTimeslotList.size() <= (index.get() + 1))
            return null;

        DictDataRespDTO dictDataRespDTO = dictBookTimeslotList.get(index.get() + 1);
        return Integer.valueOf(dictDataRespDTO.getValue());
    }

}

